package com.thinkingcoder.tccache.core.operator;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Objects;
import java.util.stream.Stream;

import com.thinkingcoder.tccache.core.model.CacheEntry;

/**
 * hash operator.
 */
public class HashOperator extends AbstractOperator {

    public int hSet(String key, String[] fields, String[] values) {
        if (fields == null || fields.length == 0) {
            return 0;
        }

        CacheEntry<?> cacheEntry = getOrInitCacheEntry(key);
        LinkedHashMap<String, String> exist = (LinkedHashMap<String, String>)cacheEntry.getValue();
        int ret = 0;
        for (int i = 0; i < fields.length; i++) {
            if (exist.put(fields[i], values[i]) == null) {
                ret++;
            }
        }
        return ret;
    }

    public String hGet(String key, String field) {
        if (checkInvalid(key)) {
            return null;
        }
        LinkedHashMap<String, String> value = getHashEntryValue(key);
        return value == null ? null : value.get(field);
    }

    public String[] hGetAll(String key) {
        if (checkInvalid(key)) {
            return null;
        }
        LinkedHashMap<String, String> value = getHashEntryValue(key);
        return value == null ? null
            : value.entrySet().stream().flatMap(e -> Stream.of(e.getKey(), e.getValue())).toArray(String[]::new);
    }

    public String[] hMGet(String key, String[] fields) {
        if (checkInvalid(key)) {
            return null;
        }
        LinkedHashMap<String, String> value = getHashEntryValue(key);
        return value == null ? null
            : Arrays.stream(fields).flatMap(f -> Stream.of(f, value.get(f))).toArray(String[]::new);
    }

    public int hLen(String key) {
        if (checkInvalid(key)) {
            return 0;
        }
        LinkedHashMap<String, String> value = getHashEntryValue(key);
        return value == null ? 0 : value.size();
    }

    public int hExists(String key, String field) {
        if (checkInvalid(key)) {
            return 0;
        }
        LinkedHashMap<String, String> value = getHashEntryValue(key);
        return value == null ? 0 : (value.containsKey(field) ? 1 : 0);
    }

    public int hDel(String key, String... fields) {
        if (checkInvalid(key)) {
            return 0;
        }
        LinkedHashMap<String, String> value = getHashEntryValue(key);
        return value == null ? 0 : (int)Arrays.stream(fields).map(value::remove).filter(Objects::nonNull).count();
    }

    private CacheEntry<?> getOrInitCacheEntry(String key) {
        CacheEntry<?> cacheEntry = getCacheEntry(key);
        if (cacheEntry == null) {
            synchronized (getMap()) {
                if ((cacheEntry = getCacheEntry(key)) == null) {
                    cacheEntry =
                        new CacheEntry<>(new LinkedHashMap<String, String>(16), System.currentTimeMillis(), -1000L);
                    putCacheEntry(key, cacheEntry);
                }
            }
        }
        return cacheEntry;
    }

    private LinkedHashMap<String, String> getHashEntryValue(String key) {
        return (LinkedHashMap<String, String>)getCacheEntryValue(key);
    }

}
